#include    "response.h"
#include    "errorpage.h"
#include    "../utils/logger.h"
#include    "../utils/os.h"
#include    "../utils/datetime.h"

#include	<fcntl.h>
#include    <microhttpd.h>

extern "C" {
#include    <luajit.h>
#include    <lauxlib.h>
#include    <lualib.h>
}

#if defined(_WIN32)
#	include	<io.h>
#else
#	include	<unistd.h>
#	include	<sys/stat.h>
#endif

#define OMNI_VERSION_INFO   "Omni Web 4.7"

Response::Response(struct MHD_Connection * conn)
    : _conn(conn)
    , _http_code(MHD_HTTP_OK)
    , _headers()
    , _cookies()
    , _body() {
	_headers[MHD_HTTP_HEADER_CONTENT_TYPE] = "text/plain";
    _headers[MHD_HTTP_HEADER_SERVER] = OMNI_VERSION_INFO;
}

void Response::Prepare(struct lua_State * lua) {
    Response ** ptr = (Response **)(lua_newuserdata(lua, sizeof(Response *)));
	(*ptr) = this;

	luaL_getmetatable(lua, "__omni_response");
	lua_setmetatable(lua, -2);
}

void Response::Flush() {
    MHD_Response * rsp = MHD_create_response_from_buffer(
		_body.size(),
		(void *)_body.c_str(),
		MHD_RESPMEM_MUST_COPY);

    if (!rsp) {
        Logger::Instance().Error("Failed to create response!");
        return;
    }

    for (auto & kv : _headers) MHD_add_response_header(rsp, kv.first.c_str(), kv.second.c_str());
    for (auto & co : _cookies) MHD_add_response_header(rsp, "Set-cookie", co.c_str());

    if (MHD_queue_response(_conn, _http_code, rsp) != MHD_YES)
        Logger::Instance().Error("Failed to queue response!");

    MHD_destroy_response(rsp);

    _http_code = MHD_HTTP_OK;
	_headers[MHD_HTTP_HEADER_CONTENT_TYPE] = "text/plain";
    _cookies.clear();
    _body.clear();
}

void Response::Error(int http_code) {
    _http_code	= http_code;
	_body	    = ErrorPage::Instance().Get(http_code);
    
	_headers[MHD_HTTP_HEADER_CONTENT_TYPE] = "text/html";
}

void Response::Redirect(const std::string & url) {
    Header(MHD_HTTP_HEADER_LOCATION, url.c_str());
	Header(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
	Header(MHD_HTTP_HEADER_CACHE_CONTROL, "no-cache");
    
    _http_code = MHD_HTTP_FOUND;
    _body.clear();
}

void Response::File(const std::string & url) {
    std::string path = "." + url;
    int fd = -1;
    struct stat info;

    if (!Exists(path.c_str())) {
        Error(MHD_HTTP_NOT_FOUND);
        Flush();
    } else if ((fd = open(path.c_str(), O_RDONLY)) == -1 || 0 != fstat(fd, &info) || !(info.st_mode & S_IFREG)) {
        if (fd >= 0) close(fd);
        Error(MHD_HTTP_FORBIDDEN);
        Flush();
    } else {
        MHD_Response * rsp = MHD_create_response_from_fd64(info.st_size, fd);
        std::string modify = GMTime(info.st_mtime);

        if (!rsp) {
            Logger::Instance().Error("Failed to create response!");
            close(fd);
            return;
        }

        MHD_add_response_header(rsp, MHD_HTTP_HEADER_LAST_MODIFIED, modify.c_str());
        const char * last_modify = MHD_lookup_connection_value(_conn, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE);
        if (!last_modify || modify != last_modify) {
            _http_code = MHD_HTTP_OK;

            std::string mime = "application/octet-stream";
            std::string ext = path.substr(path.find_last_of('.') + 1);

            if (ext == "js") mime = "text/javascript";
			else if (ext == "css") mime = "text/css";
			else if (ext == "htm" || ext == "html") mime = "text/html";
			else if (ext == "txt") mime = "text/plain";
			else if (ext == "jpg" || ext == "jpeg") mime = "image/jpeg";
			else if (ext == "gif") mime = "image/gif";
			else if (ext == "png") mime = "image/png";
			else if (ext == "bmp") mime = "image/bmp";
			else if (ext == "ico") mime = "image/x-ico";
			else if (ext == "svg") mime = "image/svg+xml";
			else if (ext == "tif" || ext == "tiff") mime = "image/tiff";
			else if (ext == "swf") mime = "application/x-shockwave-flash";

            MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE, mime.c_str());
			MHD_add_response_header(rsp, MHD_HTTP_HEADER_CACHE_CONTROL, "private, max-age=30");
            MHD_add_response_header(rsp, MHD_HTTP_HEADER_SERVER, OMNI_VERSION_INFO);
        } else {
            _http_code = MHD_HTTP_NOT_MODIFIED;
        }

        if (MHD_queue_response(_conn, _http_code, rsp) != MHD_YES)
            Logger::Instance().Error("Failed to queue response!");
            
        MHD_destroy_response(rsp);
    }
}

